import { EqualToBaseEnvTypeExecutor } from "./equalToBaseEnvTypeExecutor"
import { addNetworkEuqlStepToMap } from "../common/listenPageRequest"
import { genKeyValueObjectFromQueryString } from "../common/urlUtil"
import { tryExecute } from "../common/tryExecute"
import { resolveSelector } from "../common/resolveSelector"

const getElementTextContent = async (page, selector) => page.$eval(selector, element => {
    let value = ''
    switch (element.nodeName) {
        case 'INPUT':
        case 'TEXTAREA':
            value = element.value
            break
        default:
            value = element.textContent || ''
            break
    }

    return value.trim()
})

export const AssertTypeExecutor = {
    textContentContains: async ({ page, data, opts }) => {
        const { text: expected = '' } = data
        const selector = resolveSelector(data)
        const { timeout = 10000 } = opts

        let valid = true
        let message = ''
        let actual = ''

        try {
            await page.waitFor((selector, expectedText) => {
                const el = document.querySelector(selector)
                return el ? ((el.nodeName === 'INPUT' || el.nodeName === 'TEXTAREA') ? el.value : el.textContent).indexOf(expectedText) >= 0 : false
            }, { timeout }, selector, expected)
        } catch (error) {
            console.error(error)
        }

        actual = await getElementTextContent(page, selector)
        valid = actual.indexOf(expected) >= 0   // 包含文本值
        message = `expected: ${expected}, actual: ${actual}`

        return {
            valid,
            expected,
            actual,
            message
        }
    },
    textContentEqualTo: async ({ planId, suiteId, page, data, opts }) => {
        const { element, text: expected = '' } = data
        const selector = resolveSelector(data)
        const { timeout = 10000 } = opts

        let valid = true
        let message = ''
        let actual = ''

        try {
            await page.waitFor((selector, expectedText) => {
                const el = document.querySelector(selector)
                return el ? ((el.nodeName === 'INPUT' || el.nodeName === 'TEXTAREA') ? el.value : el.textContent) === expectedText : false
            }, { timeout }, selector, expected)
        } catch (error) {
            console.error(error)
        }

        actual = await getElementTextContent(page, selector)
        valid = actual === expected     // 文本值相同
        message = `expected: ${expected}, actual: ${actual}`

        return {
            valid,
            expected,
            actual,
            message
        }
    },
    networkRequestParamsEqualTo: async ({ planId, suiteId, page, data, opts }) => {
        let actualObjectString = await new Promise((resolve) => {
            addNetworkEuqlStepToMap({ planId, suiteId, data, opts, resolve })
        })

        const actualObject = JSON.parse(actualObjectString)
        const expectedObject = genKeyValueObjectFromQueryString(data.compareParamNames)
        const valid = Object.keys(expectedObject).every(key => expectedObject[key] === actualObject[key])

        return {
            valid,
            expected: expectedObject,
            actual: actualObject,
            message: `expected: ${JSON.stringify(expectedObject)}, actual: ${JSON.stringify(actualObject)}`
        }
    },
    ifElementExist: async ({ planId, suiteId, page, data, opts }) => {
        const { existElement } = data
        const expectExist = existElement === 'Y'
        const selector = resolveSelector(data)
        const { timeout = 10000 } = opts

        let valid = true
        let message = ''

        await tryExecute(async () => {
            if (expectExist) {
                await page.waitFor(elSelector => !!document.querySelector(elSelector), { timeout }, selector)
            } else {
                await page.waitFor(elSelector => !document.querySelector(elSelector), { timeout }, selector)
            }
        })

        const actualExist = await page.evaluate(elSelector => !!document.querySelector(elSelector), selector)
        valid = expectExist ? actualExist : !actualExist

        message = `expected: ${expectExist ? '存在' : '不存在'}, actual: ${actualExist ? '存在' : '不存在'}`

        return {
            valid,
            expected: existElement,
            actual: actualExist ? 'Y' : 'N',
            message
        }
    },
    ifElementVisible: async ({ planId, suiteId, page, data, opts }) => {
        const { element, elementIsVisible } = data
        const expectVisible = elementIsVisible === 'Y'
        const selector = resolveSelector(data)
        const { timeout = 10000 } = opts

        let valid = true
        let message = ''

        await tryExecute(async () => {
            if (expectVisible) {
                await page.waitFor(elSelector => document.querySelector(elSelector) && document.querySelector(elSelector).offsetParent, { timeout }, selector)
            } else {
                await page.waitFor(elSelector => !document.querySelector(elSelector) || !document.querySelector(elSelector).offsetParent, { timeout }, selector)
            }
        })

        const actualVisible = await page.evaluate(elSelector => document.querySelector(elSelector) ? !!document.querySelector(elSelector).offsetParent : false, selector)
        valid = expectVisible ? actualVisible : !actualVisible

        message = `expected: ${expectVisible ? '可见' : '不可见'}, actual: ${actualVisible ? '可见' : '不可见'}`

        return {
            valid,
            expected: elementIsVisible,
            actual: actualVisible ? 'Y' : 'N',
            message
        }
    },
    equalToBaseEnv: async (params) => {
        const { equalCompareType } = params.data

        const executor = EqualToBaseEnvTypeExecutor[equalCompareType]

        let result = null
        if (executor) {
            result = await executor(params)
        } else {
            throw `not supported equalCompareType: ${equalCompareType}, only support ${Object.keys(EqualToBaseEnvTypeExecutor).join(', ')}`
        }

        return result
    }
}